﻿
using Arction.Wpf.SemibindableCharting;
using Arction.Wpf.SemibindableCharting.Axes;
using Arction.Wpf.SemibindableCharting.Series3D;
using Arction.Wpf.SemibindableCharting.SeriesXY;
using Arction.Wpf.SemibindableCharting.Views.View3D;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;


namespace WPF_ExampleSpectrum3D_Demo
{
    public class MainWindowViewModel : DependencyObject
    {
        private bool _surface = false;
        private bool _surfaceAndArea = true;
        private bool _area = false;

        public event PropertyChangedEventHandler PropertyChanged;

        private DelegateCommand _startCommand = null;
        private DelegateCommand _stopCommand = null;

        private DelegateCommand _checkBoxFadeAway = null;
        private DelegateCommand _checkBoxDisableDepthTest = null;

        public ICommand StartCommand
        {
            get { return _startCommand; }
        }
        public ICommand StopCommand
        {
            get { return _stopCommand; }
        }
        public ICommand CheckBoxFadeAwayCommand
        {
            get { return _checkBoxFadeAway; }
        }
        public ICommand CheckBoxDisableDepthTestCommand
        {
            get { return _checkBoxDisableDepthTest; }
        }
        public bool IsSurface
        {
            get { return _surface; }
            set
            {
                if (value != _surface)
                {
                    _surface = value;

                    NotifyPropertyChanged("IsSurface");

                    if (value == true)
                    {
                    }
                }
            }
        }
        public bool IsSurfaceAndArea
        {
            get { return _surfaceAndArea; }
            set
            {
                if (value != _surfaceAndArea)
                {
                    _surfaceAndArea = value;

                    NotifyPropertyChanged("IsSurfaceAndArea");

                    if (value == true)
                    {

                    }
                }
            }
        }
        public bool IsArea
        {
            get { return _area; }
            set
            {
                if (value != _area)
                {
                    _area = value;

                    NotifyPropertyChanged("IsArea");

                    if (value == true)
                    {
                    }
                }
            }
        }
        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #region Dependency Property
        public static readonly DependencyProperty Lights3DProperty =
        DependencyProperty.Register(
        "lights3D",
        typeof(Light3DCollection),
        typeof(MainWindowViewModel)
       );

        public Light3DCollection lights3D
        {
            get { return (Light3DCollection)GetValue(Lights3DProperty); }
            set { SetValue(Lights3DProperty, value); }
        }

        public static readonly DependencyProperty Axisx3dProperty =
          DependencyProperty.Register(
          "axisx3d",
          typeof(AxisX3D),
          typeof(MainWindowViewModel)
        );

        public AxisX3D axisx3d
        {
            get { return (AxisX3D)GetValue(Axisx3dProperty); }
            set { SetValue(Axisx3dProperty, value); }
        }

        public static readonly DependencyProperty Axisy3dProperty =
          DependencyProperty.Register(
          "axisy3d",
          typeof(AxisY3D),
          typeof(MainWindowViewModel)
        );

        public AxisY3D axisy3d
        {
            get { return (AxisY3D)GetValue(Axisy3dProperty); }
            set { SetValue(Axisy3dProperty, value); }
        }

        public static readonly DependencyProperty Axisz3dProperty =
           DependencyProperty.Register(
           "axisz3d",
           typeof(AxisZ3D),
           typeof(MainWindowViewModel)
         );

        public AxisZ3D axisz3d
        {
            get { return (AxisZ3D)GetValue(Axisz3dProperty); }
            set { SetValue(Axisz3dProperty, value); }
        }
        public static readonly DependencyProperty SurfaceGridSeries3DProperty =
            DependencyProperty.Register(
            "surfaceGridSeries3D",
            typeof(SurfaceGridSeries3DCollection),
            typeof(MainWindowViewModel)
        );

        public SurfaceGridSeries3DCollection surfaceGridSeries3D
        {
            get { return (SurfaceGridSeries3DCollection)GetValue(SurfaceGridSeries3DProperty); }
            set { SetValue(SurfaceGridSeries3DProperty, value); }
        }
        public static readonly DependencyProperty WaterfallSeries3DProperty =
            DependencyProperty.Register(
            "waterfallSeries3D",
            typeof(WaterfallSeries3DCollection),
            typeof(MainWindowViewModel)
        );

        public WaterfallSeries3DCollection waterfallSeries3D
        {
            get { return (WaterfallSeries3DCollection)GetValue(WaterfallSeries3DProperty); }
            set { SetValue(WaterfallSeries3DProperty, value); }
        }
        public static readonly DependencyProperty textBoxHeightProperty =
       DependencyProperty.Register(
           "TextBoxHeight",
           typeof(int),
           typeof(MainWindowViewModel),
           new PropertyMetadata(100)
       );

        public int TextBoxHeight
        {
            get { return (int)GetValue(textBoxHeightProperty); }
            set { SetValue(textBoxHeightProperty, value); }
        }

        public static readonly DependencyProperty textBoxWidthProperty =
         DependencyProperty.Register(
             "TextBoxWidth",
             typeof(int),
             typeof(MainWindowViewModel),
             new PropertyMetadata(100)
         );

        public int TextBoxWidth
        {
            get { return (int)GetValue(textBoxWidthProperty); }
            set { SetValue(textBoxWidthProperty, value); }
        }
        public static readonly DependencyProperty checkBoxSuppressSurfaceLightingProperty =
            DependencyProperty.Register(
           "CheckBoxSuppressSurfaceLighting",
           typeof(bool),
           typeof(MainWindowViewModel),
           new PropertyMetadata(
               false,
               new PropertyChangedCallback(checkBoxSuppressSurfaceLightingChanged)
            )
        );

        private static void checkBoxSuppressSurfaceLightingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MainWindowViewModel example = d as MainWindowViewModel;
            bool isSuppressSurfaceLighting = (bool)e.NewValue;

            foreach (SurfaceSeries3DBase surface in (Application.Current.MainWindow as MainWindow)._chart.View3D.GetAll3DSurfaceSeries())
            {
                if (isSuppressSurfaceLighting == true)
                {
                    surface.SuppressLighting = true;
                }
                else
                {
                    surface.SuppressLighting = false;

                }
            }
        }

        public bool CheckBoxSuppressSurfaceLighting
        {
            get { return (bool)GetValue(checkBoxSuppressSurfaceLightingProperty); }
            set { SetValue(checkBoxSuppressSurfaceLightingProperty, value); }
        }
        public static readonly DependencyProperty checkBoxFadeAwayProperty =
            DependencyProperty.Register(
           "CheckBoxFadeAway",
           typeof(bool),
           typeof(MainWindowViewModel),
           new PropertyMetadata(
               true
            )
        );

        public bool CheckBoxFadeAway
        {
            get { return (bool)GetValue(checkBoxFadeAwayProperty); }
            set { SetValue(checkBoxFadeAwayProperty, value); }
        }
        public static readonly DependencyProperty checkBoxShowFloorProperty =
            DependencyProperty.Register(
           "CheckBoxShowFloor",
           typeof(bool),
           typeof(MainWindowViewModel),
           new PropertyMetadata(
               true,
               new PropertyChangedCallback(checkBoxShowFloorChanged)

            )
        );

        private static void checkBoxShowFloorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MainWindowViewModel example = d as MainWindowViewModel;
            bool isShowFloor = (bool)e.NewValue;

            if (isShowFloor == true)
            {
                (Application.Current.MainWindow as MainWindow)._chart.View3D.WallOnBottom.Visible = true;
            }
            else
            {
                (Application.Current.MainWindow as MainWindow)._chart.View3D.WallOnBottom.Visible = false;
            }
        }

        public bool CheckBoxShowFloor
        {
            get { return (bool)GetValue(checkBoxShowFloorProperty); }
            set { SetValue(checkBoxShowFloorProperty, value); }
        }
        public static readonly DependencyProperty checkBoxDisableDepthTestProperty =
            DependencyProperty.Register(
           "CheckBoxDisableDepthTest",
           typeof(bool),
           typeof(MainWindowViewModel),
           new PropertyMetadata(
               false
            )
        );

        public bool CheckBoxDisableDepthTest
        {
            get { return (bool)GetValue(checkBoxDisableDepthTestProperty); }
            set { SetValue(checkBoxDisableDepthTestProperty, value); }
        }
        #endregion

        #region Properties
        private Model _model;
        private int width = 0;
        private int iHeight = 0;
        private Model.RowDataGenerator rowDataGenerator1;
        public double m_dCurrentZ = 0.0;
        public double m_dStepZ = 0.1;
        public int m_dCurrentCount = 0;
        #endregion

        #region Constructor
        public MainWindowViewModel()
        {
            _startCommand = new DelegateCommand(ButtonStart);
            _stopCommand = new DelegateCommand(ButtonStop);

            _checkBoxFadeAway = new DelegateCommand(CheckBoxFadeAwayMethod);
            _checkBoxDisableDepthTest = new DelegateCommand(CheckBoxDisableDepthTestMethod);

            _model = new Model();

            surfaceGridSeries3D = new SurfaceGridSeries3DCollection();
            waterfallSeries3D = new WaterfallSeries3DCollection();

            (Application.Current.MainWindow as MainWindow).Loaded += ViewModel_Loaded;
        }

        private void CheckBoxFadeAwayMethod(object obj)
        {
            List<SeriesBase3D> list = (Application.Current.MainWindow as MainWindow)._chart.View3D.GetAll3DSeries();
            foreach (SeriesBase3D series in list)
            {
                if (series is Arction.Wpf.Charting.Series3D.SurfaceSeries3DBase)
                {
                    (series as SurfaceSeries3DBase).FadeAway = CheckBoxFadeAway ? 100 : 0;
                }
            }
        }

        private void CheckBoxDisableDepthTestMethod(object obj)
        {
            List<SeriesBase3D> list = (Application.Current.MainWindow as MainWindow)._chart.View3D.GetAll3DSeries();
            foreach (SeriesBase3D series in list)
            {
                if (series is SurfaceSeries3DBase)
                {
                    (series as SurfaceSeries3DBase).DisableDepthTest = CheckBoxDisableDepthTest;
                }
            }
        }

        private void ButtonStop(object obj)
        {
            rowDataGenerator1.StopButton();
        }

        private void ButtonStart(object obj)
        {
            rowDataGenerator1.StartButton();
        }

        private void ViewModel_Loaded(object sender, RoutedEventArgs e)
        {

            lights3D = View3D.CreateDefaultLights();

            axisx3d = _model.GetAxisX3D();
            axisy3d = _model.GetAxisY3D();
            axisz3d = _model.GetAxisZ3D();

            (Application.Current.MainWindow as MainWindow)._chart.View3D.Camera.Target.SetValues(-9.5f, -10f, -5.8f);

            rowDataGenerator1 = _model.rowDataGenerator;
            rowDataGenerator1.IntervalMs = 25;
            rowDataGenerator1.Started += new EventHandler(rowDataGenerator1_Started);
            rowDataGenerator1.Stopped += new EventHandler(rowDataGenerator1_Stopped);
            rowDataGenerator1.NewDataGenerated += rowDataGenerator_NewDataGenerated;

            (Application.Current.MainWindow as MainWindow).textBoxExVariation.TextChanged += TextBoxExVariation_TextChanged;
            (Application.Current.MainWindow as MainWindow).textBoxExInterval.TextChanged += TextBoxExInterval_TextChanged;
            (Application.Current.MainWindow as MainWindow).textBoxHeight.TextChanged += TextBoxHeight_TextChanged;
            (Application.Current.MainWindow as MainWindow).textBoxWidth.TextChanged += TextBoxWidth_TextChanged;

            Start();
        }



        private void TextBoxWidth_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            int wid;
            if (int.TryParse((Application.Current.MainWindow as MainWindow).textBoxWidth.Text, out wid))
            {
                TextBoxWidth = wid;
                width = wid;
            }
            else
            {
                MessageBox.Show(Application.Current.MainWindow, "Invalid width and/or height values. Using 100 x 100 instead.", "", MessageBoxButton.OK, MessageBoxImage.Warning);
                width = 100;
                TextBoxWidth = width;
            }
        }

        private void TextBoxHeight_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            int height;
            if (int.TryParse((Application.Current.MainWindow as MainWindow).textBoxHeight.Text, out height))
            {
                TextBoxHeight = height;
                width = height;
            }
            else
            {
                MessageBox.Show(Application.Current.MainWindow, "Invalid width and/or height values. Using 100 x 100 instead.", "", MessageBoxButton.OK, MessageBoxImage.Warning);
                iHeight = 100;
                TextBoxHeight = iHeight;
            }
        }

        private void TextBoxExVariation_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            int iVariation;
            if (int.TryParse((Application.Current.MainWindow as MainWindow).textBoxExVariation.Text, out iVariation))
            {
                rowDataGenerator1.VariationTextChanged(iVariation);
            }
        }

        private void TextBoxExInterval_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            int iInterval;
            if (int.TryParse((Application.Current.MainWindow as MainWindow).textBoxExInterval.Text, out iInterval))
            {
                rowDataGenerator1.IntervalTextChanged(iInterval);
            }
        }

        internal bool IsRunning
        {
            get
            {
                return rowDataGenerator1.IsRunning;
            }
        }

        public void Start()
        {
            if (IsRunning == false)
            {
                rowDataGenerator1.Start();
            }
        }

        public void Stop()
        {
            if (IsRunning == true)
            {
                rowDataGenerator1.Stop();
            }
        }
        private void DeleteAllSeries()
        {

            if (surfaceGridSeries3D != null)
            {
                surfaceGridSeries3D.Clear();
            }

            if (waterfallSeries3D != null)
            {
                waterfallSeries3D.Clear();
            }

            (Application.Current.MainWindow as MainWindow)._chart.InvalidateVisual();
        }
        private void rowDataGenerator1_Stopped(object sender, EventArgs e)
        {
            (Application.Current.MainWindow as MainWindow).groupBoxViewMode.IsEnabled = true;
        }

        private void rowDataGenerator1_Started(object sender, EventArgs e)
        {
            DeleteAllSeries();
            (Application.Current.MainWindow as MainWindow).groupBoxViewMode.IsEnabled = false;

            //Disable rendering, strongly recommended before updating chart properties
            (Application.Current.MainWindow as MainWindow)._chart.BeginUpdate();

            try
            {
                iHeight = TextBoxHeight;
                width = TextBoxWidth;
            }
            catch
            {
                MessageBox.Show(Application.Current.MainWindow, "Invalid width and/or height values. Using 100 x 100 instead.", "", MessageBoxButton.OK, MessageBoxImage.Warning);

                iHeight = 100;
                width = 100;

                TextBoxHeight = iHeight;
                TextBoxWidth = width;
            }

            double dAxisZMin = rowDataGenerator1.IntervalMs / 1000.0;
            double dAxisZMax = iHeight * (double)rowDataGenerator1.IntervalMs / 1000.0;
            m_dStepZ = rowDataGenerator1.IntervalMs / 1000.0;
            axisz3d.SetRange(dAxisZMin, dAxisZMax);
            m_dCurrentZ = dAxisZMax;

            try
            {
                //if ((IsSurface == true) || (IsSurfaceAndArea == true))
                //{

                //    surfaceGridSeries3D.Add(_model.GetSurfaceGridSeries3D(axisx3d, axisz3d));

                //    //surfaceGridSeries3D[0].SetSize(width, iHeight);
                //    surfaceGridSeries3D[0].FadeAway = CheckBoxFadeAway == true ? 100 : 0;
                //    surfaceGridSeries3D[0].SuppressLighting = CheckBoxSuppressSurfaceLighting;
                //    if (CheckBoxDisableDepthTest == true)
                //    {
                //        surfaceGridSeries3D[0].DisableDepthTest = true;
                //    }
                //    else
                //    {
                //        surfaceGridSeries3D[0].DisableDepthTest = false;
                //    }


                //}
                if (IsArea == true)
                {

                    waterfallSeries3D.Add(_model.GetWaterfallSeries3D1(axisy3d));

                    waterfallSeries3D[0].SetSize(width, iHeight);
                    waterfallSeries3D[0].FadeAway = CheckBoxFadeAway == true ? 100 : 0;
                    waterfallSeries3D[0].SuppressLighting = CheckBoxSuppressSurfaceLighting;
                    if (CheckBoxDisableDepthTest == true)
                    {
                        waterfallSeries3D[0].DisableDepthTest = true;
                    }
                    else
                    {
                        waterfallSeries3D[0].DisableDepthTest = false;
                    }
                    //Init data X, Y and Z values
                    SurfacePoint[,] areaData = waterfallSeries3D[0].Data;
                    int iColCount = waterfallSeries3D[0].SizeX;
                    int rows = waterfallSeries3D[0].SizeZ;

                    double x = axisx3d.Minimum;
                    double stepX;
                    if (iColCount > 1)
                    {
                        stepX = (axisx3d.Maximum - axisx3d.Minimum) / (iColCount - 1);
                    }
                    else
                    {
                        stepX = 0;
                    }

                    double z = axisz3d.Minimum;
                    double stepZ;
                    if (rows > 1)
                    {
                        stepZ = (axisz3d.Maximum - axisz3d.Minimum) / (rows - 1);
                    }
                    else
                    {
                        stepZ = 0;
                    }

                    double y = waterfallSeries3D[0].InitialValue;
                    for (int iCol = 0; iCol < iColCount; iCol++)
                    {
                        z = axisz3d.Minimum;
                        for (int row = 0; row < rows; row++)
                        {
                            areaData[iCol, row].X = x;
                            areaData[iCol, row].Y = y;
                            areaData[iCol, row].Z = z;
                            z += stepZ;
                        }
                        x += stepX;
                    }
                    waterfallSeries3D[0].InvalidateData();
                    waterfallSeries3D[0].ContourLineType = WaterfallContourLineType.None;
                    waterfallSeries3D[0].WireframeType = WaterfallWireframeType.None;
                }

                //if ((IsSurfaceAndArea == true) || (IsSurfaceAndArea == true))
                //{
                //    waterfallSeries3D.Add(_model.GetWaterfallSeries3D2(axisy3d));

                //    //waterfallSeries3D[0].SetSize(width, 1);
                //    waterfallSeries3D[0].FadeAway = CheckBoxFadeAway == true ? 100 : 0;
                //    waterfallSeries3D[0].SuppressLighting = CheckBoxSuppressSurfaceLighting;
                //    if (CheckBoxDisableDepthTest == true)
                //    {
                //        waterfallSeries3D[0].DisableDepthTest = true;
                //    }
                //    else
                //    {
                //        waterfallSeries3D[0].DisableDepthTest = false;
                //    }
                //    //Init one row
                //    //SurfacePoint[,] areaData = waterfallSeries3D[0].Data;
                //    int iColCount = waterfallSeries3D[0].SizeX;
                //    double x = axisx3d.Minimum;
                //    double stepX;
                //    if (iColCount > 1)
                //    {
                //        stepX = (axisx3d.Maximum - axisx3d.Minimum) / (iColCount - 1);
                //    }
                //    else
                //    {
                //        stepX = 0;
                //    }

                //    double z = axisz3d.Maximum;
                //    double y = waterfallSeries3D[0].InitialValue;
                //    for (int iCol = 0; iCol < iColCount; iCol++)
                //    {
                //        areaData[iCol, 0].X = x;
                //        areaData[iCol, 0].Y = y;
                //        areaData[iCol, 0].Z = z;
                //        x += stepX;
                //    }
                //    waterfallSeries3D[0].InvalidateData();
                //    waterfallSeries3D[0].ContourLineType = WaterfallContourLineType.None;
                //    waterfallSeries3D[0].WireframeType = WaterfallWireframeType.None;
                //}

            }
            catch
            {
                MessageBox.Show("Too big array?");
            }

            //Allow chart rendering
            (Application.Current.MainWindow as MainWindow)._chart.EndUpdate();

            rowDataGenerator1.RowLength = width;
            rowDataGenerator1.MinValue = 1;
            rowDataGenerator1.MaxValue = 95;
            rowDataGenerator1.Variation = 6;

        }

        private void rowDataGenerator_NewDataGenerated(Model.NewDataGeneratedEventArgs args)
        {
            double[] data = args.Data;

            //Disable rendering, strongly recommended before updating chart properties
            (Application.Current.MainWindow as MainWindow)._chart.BeginUpdate();

            m_dCurrentZ += m_dStepZ;
            
            //if ((IsSurface == true) || (IsSurfaceAndArea == true))
            //{
            //    SetNewDataToSurface(data);
            //}
            if (IsArea == true)
            {
                m_dCurrentCount += 1;
                SetNewDataToWaterfall(data);
            }
            //if (IsSurfaceAndArea == true)
            //{
            //    //Set the data also to front strip
            //    SetNewDataToFrontWaterfall(data);
            //}

            //Allow chart rendering
            (Application.Current.MainWindow as MainWindow)._chart.EndUpdate();
        }
        //private void SetNewDataToSurface(double[] data)
        //{
        //    if (surfaceGridSeries3D.Count == 0)
        //        return;
        //    //Surface grid series has optimized methods for adding data to back.
        //    SurfacePointMatrix data1 = surfaceGridSeries3D[0].Data;
        //    SurfacePointCollection surfacePointCollection = surfaceGridSeries3D[0].Data[0];
        //    SurfacePoint[,] surfaceData = surfaceGridSeries3D[0].Data;
        //    double minZ = m_dCurrentZ - (axisz3d.Maximum - axisz3d.Minimum);
        //    double maxZ = m_dCurrentZ;
        //    surfaceGridSeries3D[0].InsertRowBackAndScroll(data, minZ, maxZ, minZ, maxZ);
        //}

        /// <summary>
        /// Insert data to series.
        /// </summary>
        /// <param name="data">data as double array</param>
        private void SetNewDataToWaterfall(double[] data)
        {
            if (waterfallSeries3D.Count>0)
            {
                if(waterfallSeries3D[0].SizeZ < m_dCurrentCount)
                waterfallSeries3D[0].SetSize(width, iHeight += 1);

                SurfacePoint[,] areaData = waterfallSeries3D[0].Data;
                //Shift older data, drop oldest row. Set new data to back.
                //int iColCount = waterfallSeries3D[0].SizeX;
                //int rowCount = waterfallSeries3D[0].SizeZ;

                //for (int row = 0; row < rowCount - 1; row++)
                //{
                //    for (int col = 0; col < iColCount; col++)
                //    {
                //        areaData[col, row] = areaData[col, row + 1];
                //    }
                //}
                //int iNewDataCopyCount = Math.Min(data.Length, iColCount);
                double x = axisx3d.Minimum;
                double stepX = (axisx3d.Maximum - axisx3d.Minimum) / (double)(data.Length - 1);

                for (int col = 0; col < data.Length; col++)
                {
                    areaData[col, m_dCurrentCount - 1].X = x;
                    x += stepX;
                    areaData[col, m_dCurrentCount - 1].Y = data[col];
                    areaData[col, m_dCurrentCount - 1].Z = m_dCurrentZ;
                }
                waterfallSeries3D[0].InvalidateData();
                axisz3d.SetRange(m_dCurrentZ - (axisz3d.Maximum - axisz3d.Minimum), m_dCurrentZ);
            }
        }

        /// <summary>
        /// Insert data to front series.
        /// </summary>
        /// <param name="data">data as double array</param>
        //private void SetNewDataToFrontWaterfall(double[] data)
        //{
        //    if (waterfallSeries3D.Count > 0)
        //    {
        //        SurfacePoint[,] areaData = waterfallSeries3D[0].Data;

        //        //Shift older data, drop oldest row. Set new data to back.
        //        int iColCount = waterfallSeries3D[0].SizeX;

        //        int iNewDataCopyCount = Math.Min(data.Length, iColCount);

        //        for (int col = 0; col < iNewDataCopyCount; col++)
        //        {
        //            //No need to update X or Z values 
        //            areaData[col, 0].Y = data[col];
        //            areaData[col, 0].Z = m_dCurrentZ;
        //        }
        //        waterfallSeries3D[0].InvalidateData();
        //    }
        //}
    }
    #endregion
}
